﻿#region usings
using System;
using System.Linq;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.ServiceModel.DomainServices.Client;
using InsuranceAdjuster.Web.Models;
using InsuranceAdjuster.Web.Services;
using Microsoft.Windows.Data.DomainServices;
using Papa.Common;
using System.Collections.Generic;
using InsuranceAdjuster.OccasionallyConnected;
#endregion

namespace InsuranceAdjuster.Services {
    public class InsuranceAdjusterDataService : IInsuranceAdjusterDataService {
        #region Initialization
        // Used for RIA/Entity Services
        public InsuranceAdjuster.Web.Services.SLInsuranceDomainContext DataContext { get; set; }
        public event EventHandler<HasChangesEventArgs> NotifyHasChanges;


        // Used for WCF Services reatled to Branch Configuration
        private ConfigurationService.ConfigurationClient ConfigurationClient { get; set; }

        public InsuranceAdjusterDataService() {
            // Used for RIA Services
            DataContext = new SLInsuranceDomainContext(new Uri(App.BaseUri, "InsuranceAdjuster-Web-Services-SLInsuranceDomainService.svc"));
            ConfigurationClient = new ConfigurationService.ConfigurationClient();
            DataContext.PropertyChanged += ContextPropertyChanged;

        }
        private void ContextPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) {
            if (NotifyHasChanges != null) {
                NotifyHasChanges(this, new HasChangesEventArgs() { HasChanges = DataContext.HasChanges });
            }
        }
        #endregion

        #region Appointments Load Operations

        public void GetAppointments(Action<ObservableCollection<AdjusterAppointment>> callback) {
            DataContext.AdjusterAppointments.Clear();
            var query = DataContext.GetAdjusterAppointmentsQuery();
            RunAdjusterAppointmentQuery(query, callback);
        }

        public void GetAppointmentByAdjuster(Action<ObservableCollection<AdjusterAppointment>> callback, string adjuster_Id) {
            DataContext.AdjusterAppointments.Clear();
            var query = DataContext.GetAdjusterAppointmentsByAdjusterIdQuery(adjuster_Id);
            RunAdjusterAppointmentQuery(query, callback);
        }




        // Get Deltas
        public void GetAdjusterAppointmentsDeltaInformation(Action<List<KeyValuePair<IEntity, EntitySyncOperation>>> callback) {
            // Get the current, and new Anchor
            var anchorName = "Adjuster Appointments";
            var currentAnchor = EntityManager.Current.GetObjectStore().GetAnchor(anchorName);
            var newAnchor = DateTime.UtcNow;

            // Get the delta appointments (Inserts & Updates)
            // Changes since last Anchor
            var myQuery = DataContext.GetAdjusterAppointmentsByDeltaQuery(currentAnchor);
            RunAdjusterAppointmentQuery(myQuery, appointments =>
            {
                if (appointments.Count > 0) {
                    System.Diagnostics.Debug.WriteLine(string.Format( "Found {0} Added/Updated Appointments",appointments.Count.ToString()));
                }
                // Pass the result to the ViewModel to add to the list
                callback(appointments.Select(x =>
                    new KeyValuePair<IEntity, EntitySyncOperation>(
                                        x as IEntity, 
                                        EntitySyncOperation.Update)
                                        ).ToList());
            });

            // Get the deleted Appointments
            myQuery = DataContext.GetDeletedAdjusterAppointmentsQuery(currentAnchor);

            // Remove the deleted Appointments
            RunAdjusterAppointmentQuery(myQuery, appointments =>
            {
                if (appointments.Count > 0) {
                    Debug.WriteLine(string.Format("Found {0} Deleted Appointments", appointments.Count.ToString()));
                }
                callback(appointments.Select(x => 
                    new KeyValuePair<IEntity, EntitySyncOperation>(
                                        x as IEntity, 
                                        EntitySyncOperation.Delete)
                                        ).ToList());
            });

            // Save the newAnchor
            EntityManager.Current.GetObjectStore().SetAnchor(anchorName, newAnchor);
        }

        private void RunAdjusterAppointmentQuery(EntityQuery<AdjusterAppointment> query, Action<ObservableCollection<AdjusterAppointment>> callback) {
            var loadOperation = DataContext.Load<AdjusterAppointment>(query);
            loadOperation.Completed += (sender, e) =>
            {
                var appointments = new EntityList<AdjusterAppointment>(DataContext.AdjusterAppointments, loadOperation.Entities);
                callback(appointments);
            };
        }

        public void Save(Action<SubmitOperation> submitCallback, object state) {
            if (DataContext.HasChanges) {
                DataContext.SubmitChanges(submitCallback, state);
            }
        }
        public void SaveAll(Action<SubmitOperation> submitCallback, object state) {
            if (DataContext.HasChanges) {
                DataContext.SubmitChanges(submitCallback, state);
            }
        }
        #endregion

        #region Claim Load Operations

        private void RunClaimsQuery(EntityQuery<Claim> query, Action<ObservableCollection<Claim>> callback) {
            RunQuery<Claim>(query, callback, () => DataContext.Claims);
        }
        public void GetClaims(Action<ObservableCollection<Claim>> callback) {
            var query = DataContext.GetClaimsQuery();
            RunClaimsQuery(query, callback);
        }
        public void GetClaimById(Action<ObservableCollection<Claim>> callback, Guid claimGuid) {
            var query = DataContext.GetClaimByIdQuery(claimGuid);
            RunClaimsQuery(query, callback);
        }
        public void SaveClaim(Action<SubmitOperation> submitCallback, object state) {
            //TODO: possibly save just the claim
            if (DataContext.HasChanges) {
                DataContext.SubmitChanges(submitCallback, state);
            }
        }
        #endregion

        #region ClaimDamages

        public void GetClaimDamage(Action<ObservableCollection<ClaimDamage>> callback) {
            var query = DataContext.GetClaimDamagesQuery();
            RunClaimDamagesQuery(query, callback);
        }
        public void GetClaimDamageByClaimId(Action<ObservableCollection<ClaimDamage>> callback, Guid claim_Id) {
            var query = DataContext.GetClaimDamagesQuery();
            //var query = DataContext.GetClaimDamagesByClaimIdQuery(claim_Id);
            RunClaimDamagesQuery(query, callback);
        }

        private void RunClaimDamagesQuery(EntityQuery<ClaimDamage> query, Action<ObservableCollection<ClaimDamage>> callback) {
            RunQuery<ClaimDamage>(query, callback, () => DataContext.ClaimDamages);
        }


        #endregion

        #region BranchOffice Operations
        private LoadOperation<BranchOffice> _branchOfficeLoadOperation;
        private Action<ObservableCollection<BranchOffice>> _getBranchOfficeCallback;

        public void GetBranchOffice(Action<ObservableCollection<BranchOffice>> getBranchOfficeCallback) {
            ClearBranchOffice();
            var query = DataContext.GetBranchOfficesQuery();
            _getBranchOfficeCallback = getBranchOfficeCallback;
            _branchOfficeLoadOperation = DataContext.Load<BranchOffice>(query);
            _branchOfficeLoadOperation.Completed += OnLoadBranchOfficeCompleted;
        }
        private void ClearBranchOffice() {
            DataContext.BranchOffices.Clear();
        }
        private void OnLoadBranchOfficeCompleted(object sender, EventArgs e) {
            _branchOfficeLoadOperation.Completed -= OnLoadBranchOfficeCompleted;
            var query = new EntityList<BranchOffice>(DataContext.BranchOffices, _branchOfficeLoadOperation.Entities);
            _getBranchOfficeCallback(query);
        }
        public void SaveBranchStyles(Action<Models.BranchStyle> callback, object state) {
        }

        #endregion

        #region BranchStyle Load Operations
        private LoadOperation<BranchStyle> _branchStyleLoadOperation;
        private Action<Models.BranchStyle> _getBranchStyleCallback;

        public void GetBranchStyles(Action<Models.BranchStyle> callback) {
            //TODO: Incorporate User Auth, with Branch Info
            GetBranchStylesByBranchId(callback, "2000000010");
        }

        public void GetBranchStylesByBranchId(Action<Models.BranchStyle> callback, string branch_Id) {
            _getBranchStyleCallback = callback;
            ConfigurationClient.GetBranchStylesCompleted += OnGetBranchStylesCompleted;
            ConfigurationClient.GetBranchStylesAsync(branch_Id);
        }

        void OnGetBranchStylesCompleted(object sender, ConfigurationService.GetBranchStylesCompletedEventArgs e) {
            // The Service returns a configuration object with Strings
            // Must convert these to objects specific to Silverlight
            Models.BranchStyle branchStyle = new Models.BranchStyle() {
                HeadingForegroundBrush = ColorConverter.GetColorFromHex(e.Result.HeadingForegroundColor),
                HeadingBackgroundBrush = ColorConverter.GetColorFromHex(e.Result.HeadingBackgroundColor),
                BranchFont = e.Result.BranchFont

            };
            _getBranchStyleCallback(branchStyle);
        }

        #endregion

        #region ClaimStatus Load Operations

        public void GetClaimStatus(Action<ObservableCollection<ClaimStatu>> callback) {
            var query = DataContext.GetClaimStatusQuery();
            RunClaimStatuQuery(query, callback);
        }

        private void RunClaimStatuQuery(EntityQuery<ClaimStatu> query, Action<ObservableCollection<ClaimStatu>> callback) {
            RunQuery<ClaimStatu>(query, callback, () => DataContext.ClaimStatus);
        }
        #endregion

        #region StandardVehicleImage Load Operations

        public void GetStandardVehicleImageByBodyType(Action<ObservableCollection<StandardVehicleImage>> callback, string bodyType_Id) {
            var query = DataContext.GetStandardVehicleImagesQuery();
            //var query = DataContext.GetStandardVehicleImageByBodyTypeQuery(bodyType_Id);
            RunStandardVehicleImageQuery(query, callback);
        }
        public void GetVehicleInformation(Action<ObservableCollection<StandardVehicleImage>> callback) {
            var query = DataContext.GetVehicleInformationQuery();
            RunStandardVehicleImageQuery(query, callback);
        }
        public void GetStandardVehicleImages(Action<ObservableCollection<StandardVehicleImage>> callback) {
            var query = DataContext.GetStandardVehicleImagesQuery();
            RunStandardVehicleImageQuery(query, callback);
        }
        public void GetReferencePhoto(Guid photoGuid, Action<ObservableCollection<VehicleReferencePhoto>> callback) {
            var query = DataContext.GetVehicleReferencePhotoByGuidQuery(photoGuid);
            RunQuery<VehicleReferencePhoto>(query, callback, () => DataContext.VehicleReferencePhotos);
        }

        private void RunStandardVehicleImageQuery(EntityQuery<StandardVehicleImage> query, Action<ObservableCollection<StandardVehicleImage>> callback) {
            RunQuery<StandardVehicleImage>(query, callback, () => DataContext.StandardVehicleImages);
        }
        #endregion

        #region State Load Operations

        public void GetStates(Action<ObservableCollection<State>> callback) {
            var query = DataContext.GetStatesQuery();
            RunStateQuery(query, callback);
        }
        private void RunStateQuery(EntityQuery<State> query, Action<ObservableCollection<State>> callback) {
            RunQuery<State>(query, callback, () => DataContext.States);
        }
        #endregion

        #region VehicleBodyType Load Operations
        private LoadOperation<VehicleBodyType> _vehicleBodyTypeLoadOperation;
        private Action<ObservableCollection<VehicleBodyType>> _getVehicleBodyTypeCallback;

        public void GetVehicleBodyType(Action<ObservableCollection<VehicleBodyType>> callback) {
            var query = DataContext.GetVehicleBodyTypesQuery();
            RunVehicleBodyTypeQuery(query, callback);
        }

        private void RunVehicleBodyTypeQuery(EntityQuery<VehicleBodyType> query, Action<ObservableCollection<VehicleBodyType>> callback) {
            ClearVehicleBodyType();
            _getVehicleBodyTypeCallback = callback;
            _vehicleBodyTypeLoadOperation = DataContext.Load<VehicleBodyType>(query);
            _vehicleBodyTypeLoadOperation.Completed += OnLoadVehicleBodyTypeCompleted;
        }
        private void ClearVehicleBodyType() {
            DataContext.VehicleBodyTypes.Clear();
        }
        private void OnLoadVehicleBodyTypeCompleted(object sender, EventArgs e) {
            _vehicleBodyTypeLoadOperation.Completed -= OnLoadVehicleBodyTypeCompleted;
            var query = new EntityList<VehicleBodyType>(DataContext.VehicleBodyTypes, _vehicleBodyTypeLoadOperation.Entities);
            _getVehicleBodyTypeCallback(query);
        }
        #endregion

        #region VehicleYears Load Operations

        private Action<ObservableCollection<VehicleReferencePhoto>> _getVehicleImageCallback;
        private LoadOperation<VehicleReferencePhoto> _getVehicleImageLoadOperation;
        public void GetVehicleImage(Action<ObservableCollection<VehicleReferencePhoto>> callback, int year, string make, string model) {
            var query = DataContext.GetVehicleImageQuery(year, make, model);
            RunVehicleImageQuery(query, callback);
        }
        private void RunVehicleImageQuery(EntityQuery<VehicleReferencePhoto> query, Action<ObservableCollection<VehicleReferencePhoto>> callback) {
            _getVehicleImageCallback = callback;
            _getVehicleImageLoadOperation = DataContext.Load<VehicleReferencePhoto>(query);
            _getVehicleImageLoadOperation.Completed += OnLoadVehicleImageCompleted;
        }

        void OnLoadVehicleImageCompleted(object sender, EventArgs e) {
            _getVehicleImageLoadOperation.Completed -= OnLoadVehicleImageCompleted;
            var query = new EntityList<VehicleReferencePhoto>(DataContext.VehicleReferencePhotos, _getVehicleImageLoadOperation.Entities);
            _getVehicleImageCallback(query);
        }



        #endregion


        public void GetClaimDeltaInformation(Action<List<KeyValuePair<IEntity, EntitySyncOperation>>> callback) {
            GetDelta<Claim>(
                "Claims", callback
                , anchor => DataContext.GetClaimsByDeltaQuery(anchor)
                , anchor => DataContext.GetDeletedClaimsQuery(anchor)
                , (query, entityCallback) => RunClaimsQuery(query, entityCallback));
        }
        public void GetClaimDamageDeltaInformation(Action<List<KeyValuePair<IEntity, EntitySyncOperation>>> callback) {
            GetDelta<ClaimDamage>(
                "Claim Damages", callback
                , anchor => DataContext.GetClaimDamagesByDeltaQuery(anchor)
                , anchor => DataContext.GetDeletedClaimDamagesQuery(anchor)
                , (query, entityCallback) => RunClaimDamagesQuery(query, entityCallback));
        }

        public void GetClaimStatusDeltaInformation(Action<List<KeyValuePair<IEntity, EntitySyncOperation>>> callback) {
            GetDelta<ClaimStatu>(
                "Claim Statuses", callback
                , anchor => DataContext.GetClaimStatusesByDeltaQuery(anchor)
                , anchor => DataContext.GetDeletedClaimStatusesQuery(anchor)
                , (query, entityCallback) => RunClaimStatuQuery(query, entityCallback));
        }

        public void GetStateDeltaInformation(Action<List<KeyValuePair<IEntity, EntitySyncOperation>>> callback) {
            GetDelta<State>(
                "States", callback
                , anchor => DataContext.GetStatesByDeltaQuery(anchor)
                , anchor => DataContext.GetDeletedStatesQuery(anchor)
                , (query, entityCallback) => RunStateQuery(query, entityCallback));
        }

        public void GetVehicleInformationDelta(Action<List<KeyValuePair<IEntity, EntitySyncOperation>>> callback) {
            GetDelta<StandardVehicleImage>(
                "Vehicle Information", callback
                , anchor => DataContext.GetVehiclesByDeltaQuery(anchor)
                , anchor => DataContext.GetDeletedVehiclesQuery(anchor)
                , (query, entityCallback) => RunStandardVehicleImageQuery(query, entityCallback));
        }

        private void RunQuery<T>(EntityQuery<T> query, Action<ObservableCollection<T>> callback, Func<EntitySet<T>> entitySet)
            where T : Entity {
            var loadOperation = DataContext.Load<T>(query);
            loadOperation.Completed += (sender, e) =>
            {
                var entities = new EntityList<T>(entitySet.Invoke(), loadOperation.Entities);
                callback(entities);
            };
        }
        private void GetDelta<T>(
              String anchorName
            , Action<List<KeyValuePair<IEntity, EntitySyncOperation>>> callback
            , Func<DateTime, EntityQuery<T>> deltaQuery
            , Func<DateTime, EntityQuery<T>> deletedQuery
            , Action<EntityQuery<T>, Action<ObservableCollection<T>>> runQuery) where T : Entity {

            var CurrentAnchor = EntityManager.Current.GetObjectStore().GetAnchor(anchorName);
            var NewAnchor = DateTime.UtcNow;
            var MyDeltaQuery = deltaQuery.Invoke(CurrentAnchor);
            var MyDeletedQuery = deletedQuery.Invoke(CurrentAnchor);

            runQuery.Invoke(MyDeltaQuery, entities =>
            {
                EntityManager.Current.GetObjectStore().SetAnchor(anchorName, NewAnchor);
                callback(entities.Select(x =>
                    new KeyValuePair<IEntity, EntitySyncOperation>(x as IEntity, EntitySyncOperation.Update)).ToList());
            });
            runQuery.Invoke(MyDeletedQuery, entities =>
            {
                EntityManager.Current.GetObjectStore().SetAnchor(anchorName, NewAnchor);
                callback(entities.Select(x =>
                    new KeyValuePair<IEntity, EntitySyncOperation>(x as IEntity, EntitySyncOperation.Delete)).ToList());
            });
        }
    }
}